package com.htu.courseService.user.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.util.StrUtil;
import cn.idev.excel.FastExcel;
import cn.idev.excel.context.AnalysisContext;
import cn.idev.excel.event.AnalysisEventListener;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.htu.courseService.common.enums.ErrorCodeEnum;
import com.htu.courseService.common.enums.RedisCachePrefix;
import com.htu.courseService.common.exception.BusinessException;
import com.htu.courseService.common.util.RedisCache;
import com.htu.courseService.common.util.SymmetricCryptoUtil;
import com.htu.courseService.user.dao.UserMapper;
import com.htu.courseService.user.dao.UserRoleMapper;
import com.htu.courseService.user.entity.User;
import com.htu.courseService.user.entity.UserRole;
import com.htu.courseService.user.service.UserService;
import com.htu.courseService.user.vo.UserDTO;
import com.htu.courseService.user.vo.UserInfo;
import com.htu.courseService.user.vo.UserPasswordDto;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.*;

@Service
@RequiredArgsConstructor
@Slf4j
public class UserServiceImpl implements RedisCachePrefix, UserService {
    private final UserMapper userMapper;
    private final UserRoleMapper userRoleMapper;
    private final RedisCache redisCache;
    @Override
    public List<String> uploadExcel(MultipartFile multipartFile) throws IOException {
        if (multipartFile == null
                || !StringUtils.hasLength(multipartFile.getOriginalFilename())) {
            throw new BusinessException(ErrorCodeEnum.EXCEL_NOT_FOUND);
        }
        List<String> failList = new ArrayList<>();
        // 这里 需要指定读用哪个class去读，然后读取第一个sheet 文件流会自动关闭
        String encode = SymmetricCryptoUtil.encode("123456");
        HashMap<String, List<String>> role = new HashMap<>();
        ArrayList<String> permission = new ArrayList<>();
        permission.add("create");
        role.put("admin",permission);
        FastExcel.read(multipartFile.getInputStream(),UserDTO.class, new AnalysisEventListener<UserDTO>() {
            @Override
            public void invoke(UserDTO user, AnalysisContext analysisContext) {
                LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
                wrapper.eq(User::getUsername,user.getUsername())
                        .or().eq(User::getEmail,user.getEmail());
                Long count = userMapper.selectCount(wrapper);
                if(count > 0){
                    failList.add(user.getUsername() + "(" +user.getEmail() + ")" + "数据未导入成功");
                }else {
                    User userInfo = BeanUtil.copyProperties(user,User.class);
                    userInfo.setPassword(encode);
                    userInfo.setCreateTime(new DateTime());
                    userInfo.setModifyTime(new DateTime());
                    userInfo.setStatus(1);
                    userMapper.insert(userInfo);
                    LambdaQueryWrapper<User> wrapperOne = new LambdaQueryWrapper<>();
                    wrapperOne.eq(User::getUsername,userInfo.getUsername());
                    User userOne = userMapper.selectOne(wrapperOne);
                    UserRole userRole = new UserRole();
                    String userRoleName = user.getRole();
                    if(userRoleName.equals("老师")){
                        userRole.setUserId(userOne.getId());
                        userRole.setRoleId(new Long(2));
                        userRoleMapper.insert(userRole);
                        //添加权限
                        UserInfo userCache = new UserInfo();
                        userCache.setUsername(user.getUsername());
                        userCache.setPassword(encode);
                        userCache.setRolesAndPermissions(role);
                        redisCache.setCacheObject(USER_INFO + userInfo.getUsername(), userCache);
                    }else if(userRoleName.equals("学生")){
                        userRole.setUserId(userOne.getId());
                        userRole.setRoleId(new Long(3));
                        userRoleMapper.insert(userRole);
                    }
                }
            }

            @Override
            public void doAfterAllAnalysed(AnalysisContext analysisContext) {
                log.info("read excel success");
            }
        }).sheet().doRead();
        return failList;
    }

    @Override
    public void registerUser(UserDTO userDTO) {
        if(Objects.isNull(userDTO) || StrUtil.isEmpty(userDTO.getUsername())){
            throw new BusinessException(ErrorCodeEnum.USERNAME_NOT_FOUND);
        }
        String username =  userDTO.getUsername();
        String email = userDTO.getEmail();
        String userRole = userDTO.getRole();
        if(StrUtil.isEmpty(userRole)){
            throw new BusinessException(ErrorCodeEnum.ROLE_NOT_FOUND);
        }
        if(StrUtil.isEmpty(email)){
            throw new BusinessException(ErrorCodeEnum.MAIL_NOT_FOUND);
        }
        String registerCode = userDTO.getRegisterCode();
        if(StrUtil.isEmpty(registerCode)){
            throw new BusinessException(ErrorCodeEnum.REGISTER_CODE_NOT_FOUND);
        }
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(User::getUsername,username);
        long count = userMapper.selectCount(wrapper);
        if(count != 0){
            throw new BusinessException(ErrorCodeEnum.USERNAME_NOT_REPEAT);
        }
        String registerCodeCache = redisCache.getCacheObject(REGISTER_CODE + email, String.class);
        if(StrUtil.isEmpty(registerCodeCache) || !StrUtil.equals(registerCode,registerCode)){
            throw new BusinessException(ErrorCodeEnum.REGISTER_CODE_NOT_REPEAT);
        }
        String encode = SymmetricCryptoUtil.encode("123456");
        User user = BeanUtil.copyProperties(userDTO,User.class);
        user.setPassword(encode);
        user.setStatus(1);
        user.setCreateTime(new Date());
        user.setModifyTime(new Date());
        int userCount = userMapper.insert(user);
        if(userCount == 0){
            throw new BusinessException(ErrorCodeEnum.USER_NOT_REGISTER);
        }
        LambdaQueryWrapper<User> wrapperUser = new LambdaQueryWrapper<>();
        wrapperUser.eq(User::getUsername,username);
        User userOne = userMapper.selectOne(wrapperUser);
        UserRole userRoleInfo = new UserRole();
        userRoleInfo.setUserId(userOne.getId());
        userRoleInfo.setRoleId(Long.valueOf(userDTO.getRole()));
        int userRoleCount = userRoleMapper.insert(userRoleInfo);
        if(userRoleCount == 0){
            throw new BusinessException(ErrorCodeEnum.USER_NOT_REGISTER);
        }
    }

    @Override
    public void uploadPassword(UserPasswordDto userPasswordDto) {
        if(Objects.isNull(userPasswordDto) || StrUtil.isEmpty(userPasswordDto.getUsername())){
            throw new BusinessException(ErrorCodeEnum.USERNAME_NOT_FOUND);
        }
        String username =  userPasswordDto.getUsername();
        String email = userPasswordDto.getEmail();
        if(StrUtil.isEmpty(email)){
            throw new BusinessException(ErrorCodeEnum.MAIL_NOT_FOUND);
        }
        String registerCode = userPasswordDto.getRegisterCode();
        if(StrUtil.isEmpty(registerCode)){
            throw new BusinessException(ErrorCodeEnum.REGISTER_CODE_NOT_FOUND);
        }
        String registerCodeCache = redisCache.getCacheObject(UPLOAD_PASSWORD + email, String.class);
        if(StrUtil.isEmpty(registerCodeCache) || !StrUtil.equals(registerCode,registerCode)){
            throw new BusinessException(ErrorCodeEnum.REGISTER_CODE_NOT_REPEAT);
        }
        String password = userPasswordDto.getPassword();
        String passwordAgain = userPasswordDto.getPasswordAgain();
        if(StrUtil.isEmpty(password) || StrUtil.isEmpty(passwordAgain)){
            throw new BusinessException(ErrorCodeEnum.PASSWORD_NOT_NULL);
        }
        if(!StrUtil.equals(password,passwordAgain)){
            throw new BusinessException(ErrorCodeEnum.PASSWORD_NOT_EQUAL);
        }
        String encode = SymmetricCryptoUtil.encode(password);
        LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper<>();
        wrapper.eq(User::getUsername,username);
        wrapper.set(User::getPassword,encode);
        int updatePassword = userMapper.update(wrapper);
        if(updatePassword == 0){
            throw new BusinessException(ErrorCodeEnum.UPDATE_PASSWORD_FAIL);
        }
    }

    @Override
    public IPage<UserDTO> getUserPage(int pageNum, int pageSize, String username, String email) {
        IPage<UserDTO> userDTOIPage = userMapper.getUserPage(new Page<>(pageNum,pageSize),username,email);
        return userDTOIPage;
    }

    @Override
    public void deleteUser(Integer id) {
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(User::getId,id);
        User user = userMapper.selectOne(wrapper);
        if(Objects.isNull(user)){
            throw new BusinessException(ErrorCodeEnum.USER_NOT_FOUND);
        }
        int count = userMapper.deleteById(id);
        if(count == 0){
            throw new BusinessException(ErrorCodeEnum.DELETE_FAIL);
        }
        LambdaQueryWrapper<UserRole> wrapperRole = new LambdaQueryWrapper<>();
        wrapperRole.eq(UserRole::getUserId,id);
        userRoleMapper.delete(wrapperRole);
    }

    @Override
    public void updateUser(UserDTO userDTO) {
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(User::getId,userDTO.getId());
        User user = userMapper.selectOne(wrapper);
        if(Objects.isNull(user)){
            throw new BusinessException(ErrorCodeEnum.USER_NOT_FOUND);
        }
        User userOne = BeanUtil.copyProperties(userDTO,User.class);
        int updateCount = userMapper.updateById(userOne);
        if(updateCount == 0){
            throw new BusinessException(ErrorCodeEnum.UPDATE_FAIL);
        }
    }

    @Override
    public UserDTO getUserDetail(Long id) {
        User user = userMapper.selectById(id);
        UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);
        return userDTO;
    }
}
